
package DB;

import Data.*;

import java.sql.PreparedStatement;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

/**
 * Mapper Klasse, die Seat Objekte auf eine relationale DB abbildet.
 * Diese Klasse erlaubt zahlreiche Operationen, um einen Seat bzw. eine Sitzplatzstruktur zu verwalten, z.B. eine LöschOperation oder eine EinfügeOperation
 * 
 * @author Stefan Fellner
 */
public class SeatMapper {

    /*
     * Um sicher zu stellen, dass nur eine Instanz von der Klasse erzeugt wird, wird die Singleton Pattern angewendet.
     * Durch das Schlüsselwort static wird dies erreicht.
     * Quelle: http://www.theserverside.de/singleton-pattern-in-java/
     */
    private static SeatMapper seatMapper = null;

    private ConnectionManager manager;

    //mittels protected wird sichergestellt, dass mit new keine neuen Instanzen erstellt werden können. Sobals ein Mapper Objekt erstellt wird, wird eine connection aufgebaut
    protected SeatMapper(){
        manager = ConnectionManager.getInstance();

    }

    /* Eine Instanz dieser Klasse sollte immer mittels der folgenden Methode erstellt werden.
     * Dadurch wird sicher gestellt, dass nur eine Instanz von dieser Klasse vorhanden ist.
     * @return DAS <code>SeatMapper</code> Objekt
     * @see seatMapper
     */
    public static SeatMapper getInstance(){

        if(seatMapper == null){
            seatMapper = new SeatMapper();
        }

        return seatMapper;

    }


//Es folgen nun alle methoden, die die Tabelle Seat verändern können, bzw. deren inhalte abrufen können


    /* Eine Seat bzw. Sitzplatzstruktur in die Datenbank einfügen.
     * @param das sitz Objekt welches hinzugefügt werden soll
     * @return das eingefügte Objekt.
     * Diese Methode wird den Primärschlüssel automatisch erzeugt. Der größte vorhandene Schlüssel wird um 1 erhöht.
     */
    public Seat insert (Seat seat){
        //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();

        try{
            // Leeres SQL-Statement (JDBC) anlegen
            Statement stmt = con.createStatement();

            /*
             * Zunächst schauen wir nach, welches der momentan größte
             * Primärschlüsselwert eines Seats ist.
             */
            ResultSet maxId = stmt.executeQuery("SELECT MAX(seatId) AS maxId " +"FROM seat ");

            //die ergebnis Tabelle muss eine maxId zurückgeben.
            if(maxId.next()){
                try{

                 //Das einzufügende Objekt seat erhält den neu errechneten Primärschlüssel
                 seat.setSeatId(maxId.getInt("maxId") + 1);

                    stmt = con.createStatement();

                     /**
                     * Jetzt erst erfolgt die tatsächliche Einfügeoperation.
                     * Die Attribute des Seats, werden aus dem übergebenen Seat mit den getter methoden geholt.
                     */
                    stmt.executeUpdate("INSERT INTO seat (seatId, numberOfAllSeats, numberOfRows, seatsPerRow) "+
                            "VALUES (" + "'"+
                            seat.getSeatId()+"'"+", "+"'"+
                            seat.getNumberOfAllSeats()+"'"+", "+"'"+
                            seat.getNumberOfRows()+"'"+", "+"'"+
                            seat.getSeatsPerRow()+"'"+")");
                }
                //falls der Zugriff auf die Klasse seat fehlschlägt.
                catch(RemoteException ex){
                    ex.printStackTrace();
                }
            }

        }
        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }
        //dieses return ist notwendig, da sich die ID des Seats im Laufe der Methode verändert.
        return seat;
    }







    /* Einen Seat updaten, d.h. die Attribute der Sitzstruktur aktualisieren.
     * @param das Seat Objekt mit den neuen Attributen
     */
     public void update (Seat seat){

         //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();

        try{

            //sql statement
            PreparedStatement stmt = con.prepareStatement("UPDATE seat "+
                    "SET numberOfAllSeats= "+"'"+seat.getNumberOfAllSeats()+"'"+", "+
                    "numberOfRows ="+"'"+seat.getNumberOfRows()+"'"+", "+
                    "seatsPerRow ="+"'"+seat.getSeatsPerRow()+"'"+
                    " WHERE seatId = "+"'"+seat.getSeatId()+"'");
            //dieser integer beinhaltet die anzahl der veränderten datensätze nach einer bestimmten UPDATE operation
            int anzahlVeraenderterDatensaetze = stmt.executeUpdate();
            }
            //falls der Zugriff auf die Klasse Seat scheitert
            catch(RemoteException ex){
                ex.printStackTrace();
            }

        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }


     /* Ein bestimmte Seat bzw. Sitzplatzstruktur aus der Datenbank löschen. In der Verwaltungsklasse werden die abhängigkeiten geregelt.
      * Das heißt dass alle zugehörigen TypeOfAircrafts, Aircrafts, FlightRs und Bookings gelöscht werden
      * @param der zu löschende Sitz bzw. Sitzplatzstruktur
      */
     public void delete (Seat seat){

        //DB-Verbindung vom Connection Manager holen
        Connection con = this.manager.getConnection();

        try {
            // Leeres SQL-Statement anlegen
            Statement stmt = con.createStatement();

            stmt.executeUpdate("DELETE FROM seat "+
                               "WHERE seatId = "+"'"+seat.getSeatId()+"'");
        }
        //falls der Zugriff auf die Klasse seat fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }

     /*Alle Seats bzw Sitzplatzstrukturen, die in der Tabelle vorhanden sind werden zurückgegeben
      *
      *@return Vector mit allen Seats
      */
     public Vector<Seat> findAll(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Seat Objekte festhält
         Vector<Seat> allSeats = new Vector<Seat>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allSeatsQueryResult = stmt.executeQuery("SELECT * FROM seat "+"ORDER BY seatId");


             
             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Seat Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allSeatsQueryResult.next()){
                 //ein neues Seat Objekt anlegen, welches in den ergebnisvektor aufgenommen wird
                 Seat seat = new SeatImpl(allSeatsQueryResult.getInt("seatsPerRow"),
                                          allSeatsQueryResult.getInt("numberOfRows"));
                 seat.setSeatId(allSeatsQueryResult.getInt("seatId"));
                 seat.setNumberOfAllSeats(allSeatsQueryResult.getInt("numberOfAllSeats"));

                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allSeats.addElement(seat);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen Seat Objeten aus der Tabelle wird zurückgegeben
         return allSeats;
     }





     /* Eine bestimmte Sitzplatzstrutur abhängig von der ID finden
      *
      * @param die ID der Sitzplatzstruktur als als integer
      * @return das gesuchte Seat bzw. Sitzplatzstrukturobjekt
      */

     public Seat findById(int seatId){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredSeat = stmt.executeQuery("SELECT * FROM seat "+
                                                           "WHERE seatId = "+"'"+seatId+"'");

             //es gibt nur einen Seat mit gleicher ID. Es wird zunächst geschaut ob, ein seat in der ResultSet "Tabelle" ist. Wenn nicht, wird NULL zurückgegeben
             if(desiredSeat.next()){
                 //den ergebnisDatensatz in ein Objekt umwandeln
                 Seat seat = new SeatImpl(desiredSeat.getInt("seatsPerRow"), desiredSeat.getInt("numberOfRows"));
                 seat.setSeatId(desiredSeat.getInt("seatId"));
                 seat.setNumberOfAllSeats(desiredSeat.getInt("numberOfAllSeats"));
                 //den gefundenen Seat zurückgeben
                 return seat;
             }

         }
         //falls der Zugriff auf die Klasse seat fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //wenn kein seat gefunden wurde und keine Fehlermeldung ausgegeben wurde, gibt die Methode null zurück.
         return null;
     }


     /* Eine bestimmte Sitzplatzstrutur abhängig von der Gesamtsitzeanzahl finden
      *
      * @param die Gesamtsitzeanzahl der Sitzplatzstruktur als als integer
      * @return Vector, mit sitzplatzstrukturen, die die übergebene Gesamtsitzeanzahl hat.
      */

     public Vector<Seat> findByNumberOfAllSeats(int numberOfAllSeats){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Seat Objekte festhält
         Vector<Seat> allSeats = new Vector<Seat>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allSeatsQueryResult = stmt.executeQuery("SELECT * FROM seat "+
                                                                    "WHERE numberOfAllSeats = "+"'"+numberOfAllSeats+"'");



             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Seat Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allSeatsQueryResult.next()){
                 //ein neues Seat Objekt anlegen, welches in den ergebnisvektor aufgenommen wird
                 Seat seat = new SeatImpl(allSeatsQueryResult.getInt("seatsPerRow"),
                                          allSeatsQueryResult.getInt("numberOfRows"));
                 seat.setSeatId(allSeatsQueryResult.getInt("seatId"));
                 seat.setNumberOfAllSeats(allSeatsQueryResult.getInt("numberOfAllSeats"));

                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allSeats.addElement(seat);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen Seat Objeten aus der Tabelle wird zurückgegeben
         return allSeats;
     }


     /* Überprüfen, ob eine bestimmte sitzplatzstruktur in irgendeinem TypeOfAircraft verbaut ist.
      *
      * @param die zu überprüfende sitzplatzstruktur
      * @return true == verbaut oder false == nicht verbaut)
      */

     public boolean isSeatUsedInAnyTypeOfAircraft(Seat seat){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredSeat = stmt.executeQuery("SELECT * FROM typeOfAircraft "+
                                                           "WHERE seatId = "+"'"+seat.getSeatId()+"'");

             //sobald es einen eintrag in der ergebnis Tabelle gibt muss true zurück gegeben werden, da die Sitzplatzstruktur in mind. 1 TypeOf Aircraft verwendet wird
             if(desiredSeat.next()){

                 return true;
             }

         }
         //falls der Zugriff auf die Klasse seat fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return true;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return true;
        }
        //wenn kein typeOfAircraft mit der zu prüfenden sitzplatzstruktur gefunden wurde, wird false zurückgegeben und das bedeutet, dass die strutur gelöscht werden kann.
         return false;
     }


}

